/*
 *  Copyright (C) Compaq Computer Corporation, 1998, 1999
 *  Copyright (C) Extenex Corporation, 2001
 *  Copyright (C) Intrinsyc, Inc., 2002
 *
 * (C) Copyright 2006 Marvell International Ltd.
 * All Rights Reserved
 *
 *  PXA USB controller core driver.
 *
 *  This file provides interrupt routing and overall coordination
 *  of the endpoints.
 *
 *  Please see:
 *    linux/Documentation/arm/SA1100/SA1100_USB
 *  for more info.
 *
 *  02-May-2002
 *   Frank Becker (Intrinsyc) - derived from sa1100 usb_ctl.c
 *
 */

#include "types.h"
#include "arch.h"
#include "time.h"
#include "mhn-regs.h"
#include "xllp_u2d.h"
#include "mhn_u2d_ctl.h"
#include "util.h"
#include "asm/errno.h"

#define CONFIG_CPU_MONAHANS_LV

#define MHN_U2D_CTRL_DBG 0
#if MHN_U2D_CTRL_DBG
#define DBGOUT(x) x
#define DUMP_U2D_REG
#else
#define DBGOUT(x)     /* gcc will remove all the debug code for us */
#endif



//////////////////////////////////////////////////////////////////////////////
// Prototypes
//////////////////////////////////////////////////////////////////////////////

int usbctl_next_state_on_event( int event );
void udc_int_hndlr(int, void *);
static void initialize_descriptors( void );
static void soft_connect_hook( int enable );
//static void xcvr_init(void);
static void u2d_disable(void);
static void u2d_enable(void);
static void u2d_configure(int speed, int phase);
static void check_usb_speed(void);
void set_config_handler(void);
void usb_driver_reset(void);
void usb_driver_speed(int speed);

#ifndef MMU_ENABLE
XLLP_DMAC_DESCRIPTOR_T  dma_desc_buf[DMA_DESC_NUM+1];
#else
XLLP_DMAC_DESCRIPTOR_T *dma_desc_buf;
int * ep1_buf;
int * ep2_buf;
#endif
P_XLLP_DMAC_DESCRIPTOR_T    ep1_dma_desc;
P_XLLP_DMAC_DESCRIPTOR_T    ep2_dma_desc;

#if CONFIG_PROC_FS
#define PROC_NODE_NAME "driver/mhnusb"
static int usbctl_read_proc(char *page, char **start, off_t off,
                            int count, int *eof, void *data);
#endif

/////////////////////////////////////////////////////////////////////////////
int usb_connected = 0;
int usb_speed = USB_SPEED_HIGH;

//////////////////////////////////////////////////////////////////////////////
// Globals
//////////////////////////////////////////////////////////////////////////////
static const char pszMe[] = "usbctl: ";
struct usb_info_t usbd_info;  /* global to ep0, usb_recv, usb_send */

/* device descriptors */
static desc_t desc;

#define MAX_STRING_DESC 16
static string_desc_t * string_desc_array[ MAX_STRING_DESC ];
static string_desc_t sd_zero;  /* special sd_zero holds language codes */

// called when configured
static usb_notify_t configured_callback = NULL;

enum {
    kStateZombie        = 0,
    kStateZombieSuspend     = 1,
    kStateDefault       = 2,
    kStateDefaultSuspend    = 3,
    kStateAddr          = 4,
    kStateAddrSuspend       = 5,
    kStateConfig        = 6,
    kStateConfigSuspend     = 7
};

XLLP_U2D_T  xllp_u2d;
#define XLLP_MFP_RM_DATABASE    ((XLLP_MFP_RM_DB_ID_T)&(U2DDR0 + 4)) // reserved

/*
 * FIXME: The PXA UDC handles several host device requests without user
 * notification/intervention. The table could be collapsed quite a bit...
 */
static int device_state_machine[8][6] = {
//              suspend               reset          resume         adddr       config        deconfig
/* zombie */  { kStateZombieSuspend , kStateDefault, kStateZombie , kError    , kError      , kError },
/* zom sus */ { kStateZombieSuspend , kStateDefault, kStateZombie , kError    , kError      , kError },
/* default */ { kStateDefaultSuspend, kStateDefault, kStateDefault, kStateAddr, kStateConfig, kError },
/* def sus */ { kStateDefaultSuspend, kStateDefault, kStateDefault, kError    , kError      , kError },
/* addr */    { kStateAddrSuspend   , kStateDefault, kStateAddr   , kError    , kStateConfig, kError },
/* addr sus */{ kStateAddrSuspend   , kStateDefault, kStateAddr   , kError    , kError      , kError },
/* config */  { kStateConfigSuspend , kStateDefault, kStateConfig , kError    , kError      , kStateDefault },
/* cfg sus */ { kStateConfigSuspend , kStateDefault, kStateConfig , kError    , kError      , kError }
};

/* "device state" is the usb device framework state, as opposed to the
   "state machine state" which is whatever the driver needs and is much
   more fine grained
*/
static int sm_state_to_device_state[8] = {
//  zombie            zom suspend
USB_STATE_POWERED, USB_STATE_SUSPENDED,
//  default           default sus
USB_STATE_DEFAULT, USB_STATE_SUSPENDED,
//  addr              addr sus
USB_STATE_ADDRESS, USB_STATE_SUSPENDED,
//  config            config sus
USB_STATE_CONFIGURED, USB_STATE_SUSPENDED
};

static int sm_state = kStateZombie;

//////////////////////////////////////////////////////////////////////////////
// Async
//////////////////////////////////////////////////////////////////////////////

/* The UDCCR reg contains mask and interrupt status bits,
 * so using '|=' isn't safe as it may ack an interrupt.
 */

void u2d_set_mask_U2DCR( int mask )
{
    U2DCR = (U2DCR & U2DCR_MASK_BITS) | (mask & U2DCR_MASK_BITS);
}

void u2d_clear_mask_U2DCR( int mask)
{
    U2DCR = (U2DCR & U2DCR_MASK_BITS) & ~(mask & U2DCR_MASK_BITS);
}

void u2d_int_hndlr(int irq, void *dev_id)
{
    __u32 isr = U2DISR;
    __u32 dma_int = U2DMAINT;

    DBGOUT(printf("%s U2DISR=%08x, U2DICR=%08x, U2DCR = %08x, U2DCSRA = %x, U2DCSRB = %x\n",
                pszMe, isr, icr, U2DCR, U2DCSRA, U2DCSRB));

    if ( !isr  && !dma_int) return;

    /* endpoint A, B DMA interrupt */
    if(dma_int & 0x06)   {
        DBGOUT(printf("dma_interrupt~~~~~~~~~~~~~dma_int : %x\n", U2DMAINT));
        U2DMAINT = dma_int & 0x06;
        if(dma_int & 0x2) {
            ep1_int_hndlr(0);
        }

        if(dma_int & 0x4) {
            ep2_int_hndlr(0);
        }

    }

    if (isr & (U2DINT_SU | U2DINT_RU | U2DINT_CC | U2DINT_RS)) {
        /* InterRupt SUSpend */
        if ( isr & U2DINT_SU ) {
            DBGOUT(printf("suspend interrupt~~~~~~~~~~~~~\n"));
            U2DISR = U2DINT_SU;
        }

        /* InterRupt ResUme */
        if ( isr & U2DINT_RU ) {
            DBGOUT(printf("resume interrupt~~~~~~~~~~~~~\n"));
            /* FIXME */
            /* udc_ack_int_UDCCR( UDCCR_RESIR); */
            U2DISR = U2DINT_RU;
            usb_connected = 0;
        }

        /* InterRupt Configuration Changed */
        if (isr & U2DINT_CC) {
            DBGOUT(printf("configuration interrupt~~~~~~~~~~~~~\n"));
            set_config_handler();
        }

        /* ReSeT Interrupt Request - UDC has been reset */
        if (isr & U2DINT_RS) {
            DBGOUT(printf("reset interrupt~~~~~~~~~~~~~\n"));
            U2DISR = U2DINT_RS;

            usb_connected = 0;
            usb_driver_reset();
        }
    }
    if (isr & (U2DINT_FIFOERR | U2DINT_PACKETCMP | U2DINT_SPACKETCMP)) {
        /* FIFO err */
        if (isr & U2DINT_FIFOERR)
            U2DISR = U2DINT_FIFOERR;
        /* ep0 int */
        if ( isr & (U2DINT_PACKETCMP | U2DINT_SPACKETCMP)) {
            DBGOUT(printf("ep0 data interrupt~~~~~~~~~~~~~\n"));
            U2DISR = U2DINT_PACKETCMP | U2DINT_SPACKETCMP;
            ep0_int_hndlr((isr & (U2DINT_PACKETCMP | U2DINT_SPACKETCMP)));
        }
    }
    if (isr & ((U2DINT_FIFOERR | U2DINT_PACKETCMP | U2DINT_SPACKETCMP) << 3)) {
        if ( isr & (U2DINT_FIFOERR << 3) ) {
            DBGOUT(printf("ep1 fifo erro interrupt~~~~~~~~~~~~~\n"));
            U2DCSRA = U2DCSR_TRN;
            U2DISR = U2DINT_FIFOERR << 3;
        }
        /* transmit bulk */
        if ( isr & ((U2DINT_PACKETCMP | U2DINT_SPACKETCMP) << 3) ) {
            DBGOUT(printf("ep1 data interrupt~~~~~~~~~~~~~\n"));
            U2DCSRA = isr & ((U2DINT_PACKETCMP | U2DINT_SPACKETCMP) << 3);
            ep1_int_hndlr(isr);
        }
    }
    if (isr & ((U2DINT_FIFOERR | U2DINT_PACKETCMP | U2DINT_SPACKETCMP) << 6)) {
        if ( isr & (U2DINT_FIFOERR << 6) ) {
            DBGOUT(printf("ep2 fifo error interrupt~~~~~~~~~~~~~\n"));
            U2DCSRB = U2DCSR_TRN;
            U2DISR = U2DINT_FIFOERR << 6;
        }

        /* receive bulk */
        if (isr & ((U2DINT_PACKETCMP | U2DINT_SPACKETCMP) << 6)) {
            DBGOUT(printf("ep2 data interrupt\n"));

            U2DISR = isr & ((U2DINT_PACKETCMP | U2DINT_SPACKETCMP) << 6);
            ep2_int_hndlr(isr);
        }
    }
}

void check_usb_speed(void)
{

        /* set usb speed */
        if(U2DCR & U2DCR_HS)
            usb_speed = USB_SPEED_HIGH;
        else
            usb_speed = USB_SPEED_FULL;
        DBGOUT(printf("change speed, U2DCR %x, usb_speed %d\n", U2DCR, usb_speed));

        u2d_configure(usb_speed, 2);

}

void dump_u2d_reg()
{
#ifdef DUMP_U2D_REG
    printf("U2D control/status/ Register:\n");
    printf("U2DCR = %x\n", U2DCR);
    printf("U2DICR = %x\n", U2DICR);
    printf("U2ISR = %x\n", U2DISR);

    printf("U2DCSR0 = %x\n", U2DCSR0);
    printf("U2DCSRA = %x\n", U2DCSRA);
    printf("U2DCSRB = %x\n", U2DCSRB);

    printf("U2D byte count Register:\n");
    printf("U2DBCR0 = %x\n", U2DBCR0);
    printf("U2DBCRA = %x\n", U2DBCRA);
    printf("U2DBCRB = %x\n", U2DBCRB);

    printf("U2DDR0 = %x\n", U2DDR0);

    printf("U2D endpoint configuration Register:\n");
    printf("U2DCRA = %x\n", U2DEPCRA);
    printf("U2DCRB = %x\n", U2DEPCRB);

    printf("U2D endpoint information Register:\n");
    printf("U2DEN0 = %x\n", U2DEN0);
    printf("U2DENA = %x\n", U2DENA);
    printf("U2DENB = %x\n", U2DENB);

    printf("U2D DMA Control Register:\n");
    printf("U2DMACSRA = %x\n", U2DMACSR(1));
    printf("U2DMACSRB = %x\n", U2DMACSR(2));

    printf("U2DMADADRA = %x\n", U2DMADADR(1));
    printf("U2DMASADRA = %x\n", U2DMASADR(1));
    printf("U2DMATADRA = %x\n", U2DMATADR(1));
    printf("U2DMACMDRA = %x\n", U2DMACMDR(1));

    printf("U2DMADADRB = %x\n", U2DMADADR(2));
    printf("U2DMASADRB = %x\n", U2DMASADR(2));
    printf("U2DMATADRB = %x\n", U2DMATADR(2));
    printf("U2DMACMDRB = %x\n", U2DMACMDR(2));
#endif
}

void set_config_handler(void)
{
    DBGOUT(printf("In New set_config_handler\n"));
    DBGOUT(printf("U2DCR = %x, U2DCRA = %x, U2DENA = %x\n", u2dcr, u2dcrA, U2DENA));

    check_usb_speed();
    ep0_reset();
    ep1_reset();
    ep2_reset();
    usb_driver_reset();

    U2DISR = U2DINT_CC;
    U2DCR = (U2DCR & (U2DCR_UDE)) | U2DCR_SMAC | U2DCR_CC;

    DBGOUT(printf("after check_usb_speed, U2DCR = %x, U2DCRA = %x, U2DENA = %x, U2DENB= %x\n", u2dcr, u2dcrA, U2DENA, U2DENB));

}

//////////////////////////////////////////////////////////////////////////////
// Public Interface
//////////////////////////////////////////////////////////////////////////////

/* Open PXA usb core on behalf of a client, but don't start running */

int pxa_usb_open( const char * client )
{
    usbd_info.client_name = (char*) client;
    memset(&usbd_info.stats, 0, sizeof(struct usb_stats_t));
    memset(string_desc_array, 0, sizeof(string_desc_array));

    /* hack to start in zombie suspended state */
    sm_state = kStateZombieSuspend;
    usbd_info.state = USB_STATE_SUSPENDED;

    /* create descriptors for enumeration */
    initialize_descriptors();

    ep1_init(0);
    return 0;
}

/* Start running. Must have called usb_open (above) first */
int pxa_usb_start( void )
{
    DBGOUT(printf("@pxa_usb_start %s\n", __FUNCTION__));

    if ( usbd_info.client_name == NULL ) {
        DBGOUT(printf( "%s%s - no client registered\n", pszMe, __FUNCTION__ ));
        return -EPERM;
    }

    //xcvr_init();
    /* start UDC internal machinery running
     * move to check_usb_connection, to only enable U2D
     * when data transfer is desired
     */
    //u2d_enable();
    //msleep(1);

#ifdef MMU_ENABLE
    dma_desc_buf = (XLLP_DMAC_DESCRIPTOR_T *)DMA_AREA_START;
    ep1_buf = ((unsigned *)dma_desc_buf + DMA_DESC_NUM * sizeof(XLLP_DMAC_DESCRIPTOR_T));
    ep2_buf = ((unsigned *)ep1_buf + sizeof(DMA_BUF_SIZE));
#endif
    /* give endpoint notification we are starting */
    ep1_state_change_notify( USB_STATE_SUSPENDED );
    ep2_state_change_notify( USB_STATE_SUSPENDED );

    /* alloc the dma desc buffer for ep1, ep2 */
    ep1_dma_desc = (P_XLLP_DMAC_DESCRIPTOR_T)((unsigned int)(&(dma_desc_buf[0])+1) & 0xfffffff0);
    ep2_dma_desc = ep1_dma_desc + DMA_DESC_NUM/2;
    /* enable any platform specific hardware */
    //soft_connect_hook( 1 );

    DBGOUT(printf("ep1 des %x, ep2 des %x\n",(unsigned long *)ep1_dma_desc,(unsigned long *)ep2_dma_desc));
    return 0;
}

/* Stop USB core from running */
int pxa_usb_stop( void )
{
    if ( usbd_info.client_name == NULL ) {
        DBGOUT(printf( "%s%s - no client registered\n",
                pszMe, __FUNCTION__ ));
        return -EPERM;
    }

    ep1_reset();
    ep2_reset();

    u2d_disable();
    DBGOUT(printf( "%sStopped %s\n", pszMe, usbd_info.client_name ));
    return 0;
}

/* Tell PXA core client is through using it */
int pxa_usb_close( void )
{
    if ( usbd_info.client_name == NULL ) {
        DBGOUT(printf( "%s%s - no client registered\n",
                  pszMe, __FUNCTION__ ));
        return -EPERM;
    }
    DBGOUT(printf( "%s%s closed.\n", pszMe, (char*)usbd_info.client_name ));
    usbd_info.client_name = NULL;
    return 0;
}

/* set a proc to be called when device is configured */
usb_notify_t pxa_set_configured_callback( usb_notify_t func )
{
    usb_notify_t retval = configured_callback;
    configured_callback = func;
    return retval;
}

/*====================================================
 * Descriptor Manipulation.
 * Use these between open() and start() above to setup
 * the descriptors for your device.
 *
 */

/* get pointer to static default descriptor */
desc_t * pxa_usb_get_descriptor_ptr( void ) { return &desc; }

/* optional: set a string descriptor */
int pxa_usb_set_string_descriptor( int i, string_desc_t * p )
{
    int retval;
    if ( i < MAX_STRING_DESC ) {
        string_desc_array[i] = p;
        retval = 0;
    }
    else {
        retval = -EINVAL;
    }
    return retval;
}

/* optional: get a previously set string descriptor */
string_desc_t * pxa_usb_get_string_descriptor( int i )
{
    return ( i < MAX_STRING_DESC )
        ? string_desc_array[i]
        : NULL;
}

config_desc_t * pxa_usb_get_config(int cfgval)
{
    int i;
    desc_t * pdesc = pxa_usb_get_descriptor_ptr();
    config_desc_t *cfg = (config_desc_t*) (pdesc->cdb);

    for( i=0; i<pdesc->dev.bNumConfigurations; i++) {
        if( cfg->bConfigurationValue == cfgval ) return cfg;
        cfg = (config_desc_t*) ((unsigned char*)cfg + cfg->wTotalLength);
    }

    return NULL;
}

intf_desc_t * pxa_usb_get_interface( config_desc_t *cfg, int idx)
{
    int i;
    intf_desc_t *intf = (intf_desc_t*) (cfg + 1);

    for( i=0; i < cfg->bNumInterfaces; i++) {
        if( idx == intf->bInterfaceNumber) return intf;
        intf++;
        if( i== 1) {
            intf= (intf_desc_t*)((unsigned long *)intf +sizeof(acm_function));
        }
    }

    return NULL;
}


ep_desc_t * pxa_usb_get_endpoint( intf_desc_t *intf, int idx)
{
    int i;
    ep_desc_t *ep = (ep_desc_t *) (intf+1);


    for( i=0; i< intf->bNumEndpoints; i++) {
        if( idx == (ep->bEndpointAddress & 0xF) ) return ep;
        ep++;
    }
    return NULL;
}

//////////////////////////////////////////////////////////////////////////////
// Exports to rest of driver
//////////////////////////////////////////////////////////////////////////////

/* called by the int handler here and the two endpoint files when interesting
   .."events" happen */

int usbctl_next_state_on_event( int event )
{
    int next_state = device_state_machine[ sm_state ][ event ];
    if ( next_state != kError )
    {
        int next_device_state = sm_state_to_device_state[ next_state ];
        DBGOUT(printf( "%s%s --> [%s] --> %s. Device in %s state.\n",
                pszMe, state_names[ sm_state ], event_names[ event ],
                state_names[ next_state ], device_state_names[ next_device_state ] ));

        sm_state = next_state;
        if ( usbd_info.state != next_device_state ) {
            if ( (int)configured_callback != 0xffffffff
                 &&
                 next_device_state == USB_STATE_CONFIGURED
                 &&
                 usbd_info.state != USB_STATE_SUSPENDED
               ) {
                DBGOUT(printf("configured_callback %x\n",configured_callback));
                configured_callback();
            }
            usbd_info.state = next_device_state;

            ep1_state_change_notify( next_device_state );
            ep2_state_change_notify( next_device_state );
        }
    }
    else
        DBGOUT(printf( "%s%s --> [%s] --> ??? is an error.\n",
                pszMe, state_names[ sm_state ], event_names[ event ] ));
    return next_state;
}

//////////////////////////////////////////////////////////////////////////////
// Private Helpers
//////////////////////////////////////////////////////////////////////////////

/* setup default descriptors */

static void initialize_descriptors(void)
{

    desc.dev.bLength               = sizeof( device_desc_t );
    desc.dev.bDescriptorType       = USB_DESC_DEVICE;
    desc.dev.bcdUSB                = 0x0200; /* 1.0 */  // HS 0x200 ?????
    desc.dev.bDeviceClass          = USB_CLASS_COMM;    /* vendor specific */
    desc.dev.bDeviceSubClass       = 0;
    desc.dev.bDeviceProtocol       = 0;
    desc.dev.bMaxPacketSize0       = 64;    /* ep0 max fifo size, 16 orig ????? */
    desc.dev.idVendor              = 0; /* vendor ID undefined */
    desc.dev.idProduct             = 0;     /* product */
    desc.dev.bcdDevice             = 0;     /* vendor assigned device release num */
    desc.dev.iManufacturer         = 0; /* index of manufacturer string */
    desc.dev.iProduct              = 0;     /* index of product description string */
    desc.dev.iSerialNumber         = 0; /* index of string holding product s/n */
    desc.dev.bNumConfigurations    = 1; /* configurations we have */

    desc.qua.bLength               = sizeof( qua_desc_t );
    desc.qua.bDescriptorType       = USB_DESC_QUALIFIER;
    desc.qua.bcdUSB                = 0x0200;
    desc.qua.bDeviceClass          = USB_CLASS_COMM;    /* vendor specific */
    desc.qua.bDeviceSubClass       = 0;
    desc.qua.bDeviceProtocol       = 0;
    desc.qua.bMaxPacketSize0       = 64;    /* ep0 max fifo size, 16 or 64 ????? */
    desc.qua.bNumConfigurations    = 1;
    desc.qua.bRESERVED             = 0;


    // FIXME: Add support for all endpoint...
    usb_driver_reset();

    /* set language */
    /* See: http://www.usb.org/developers/data/USB_LANGIDs.pdf */
    sd_zero.bDescriptorType = USB_DESC_STRING;
    sd_zero.bLength         = sizeof( string_desc_t );
    sd_zero.bString[0]      = make_word_c( 0x409 ); /* American English */
    pxa_usb_set_string_descriptor( 0, &sd_zero );
}

/* soft_connect_hook()
 * Some devices have platform-specific circuitry to make USB
 * not seem to be plugged in, even when it is. This allows
 * software to control when a device 'appears' on the USB bus
 * (after Linux has booted and this driver has loaded, for
 * example). If you have such a circuit, control it here.
 */
static void soft_connect_hook( int enable )
{
    if (enable) {
        U2DCR &= ~U2DCR_ADD;
        msleep(6);
    }
    else {
        U2DCR |= U2DCR_ADD;
        msleep(6);
    }
}

/* configure udc endpoints */
static void u2d_configure(int speed, int phase)
{
// bugbug!!!should not add printf here, or usb can't be enum
//    printf("HAHA...In u2d_configure. speed: %d, phase: %d\n", speed, phase);


    P_XLLP_U2D_EP_CONFIG ep_config;

    /* endpoint A~ BULK, IN, MPS: 64B(FS)/512B (HS),
     * configuration 1, interface 0, altsetting 0, endpoint 1 */
    ep_config = &xllp_u2d.ConfigTable[1];

    if(phase == 1) {
        ep_config->ET = USB_ENDPOINT_XFER_BULK;
        ep_config->ED = 1;
        ep_config->EN = 1;

        ep_config->CN = 2;
        ep_config->InterfaceNumber = 0;
        ep_config->AISN = 0;

//      ep_config->MPS = 512;

        ep_config->HBW = 0;
        //ep_config->bufferSize = (BULK_FIFO_SIZE % 8)?1:0;
        //ep_config->bufferSize += (BULK_FIFO_SIZE / 8);
        ep_config->bufferSize = BULK_FIFO_SIZE;

        //U2DEPCR(i) = (ep_config->bufferSize << 1 )|U2DEPCR_EE;

        //XllpU2DConfigureOneEndpoint(&dev->xllp_u2d); not actual config
        XllpU2DEndpointSetConfig(&xllp_u2d, 1, ep_config);
//      XllpU2DEndpointSetInfo(&xllp_u2d, 1, ep_config);

    }
    else if(phase == 2) {
        ep_config->MPS = ((speed)==USB_SPEED_HIGH)?((unsigned)512):((unsigned)64);
        XllpU2DEndpointSetInfo(&xllp_u2d, 1, ep_config);
    }

    /* endpoint B~ BULK, OUT, MPS: 64B(FS)/512B (HS),
     * configuration 1, interface 0, altsetting 0, endpoint 2 */
    ep_config = &xllp_u2d.ConfigTable[2];

    if(phase == 1) {
        ep_config->ET = USB_ENDPOINT_XFER_BULK;
        ep_config->ED = 0;
        ep_config->EN = 2;

        ep_config->CN = 2;
        ep_config->InterfaceNumber = 0;
        ep_config->AISN = 0;

//      ep_config->MPS = 512;

        ep_config->HBW = 0; //
        //ep_config->bufferSize = (BULK_FIFO_SIZE % 8)?1:0;
        //ep_config->bufferSize += (BULK_FIFO_SIZE / 8);
        ep_config->bufferSize = BULK_FIFO_SIZE;

        XllpU2DEndpointSetConfig(&xllp_u2d, 2, ep_config);
    }
    else if(phase == 2) {
        ep_config->MPS = ((speed)==USB_SPEED_HIGH)?((unsigned)512):((unsigned)64);
        XllpU2DEndpointSetInfo(&xllp_u2d, 2, ep_config);
    }

    /* endpoint C~ INTERRUPT, IN, MPS: 64B,
     * configuration 1, interface 0, altsetting 0, endpoint 3 */

    ep_config  = &xllp_u2d.ConfigTable[3];
    if(phase == 1) {
        ep_config->ET = USB_ENDPOINT_XFER_INT;
        ep_config->ED = 1;
        ep_config->EN = 3;

        ep_config->CN = 2;
        ep_config->InterfaceNumber = 0;
        ep_config->AISN = 0;

        ep_config->HBW = 0; //
        //ep_config->bufferSize = (BULK_FIFO_SIZE % 8)?1:0;
        //ep_config->bufferSize += (BULK_FIFO_SIZE / 8);
        ep_config->bufferSize = BULK_FIFO_SIZE;

        XllpU2DEndpointSetConfig(&xllp_u2d, 3, ep_config);
    }
    else if(phase == 2) {
        ep_config->MPS = 64;
        XllpU2DEndpointSetInfo(&xllp_u2d, 3, ep_config);
    }
}


/* disable the UDC at the source */
static void u2d_disable(void)
{
    soft_connect_hook( 0 );
    /* clear UDC-enable */
    //udc_clear_mask_UDCCR( UDCCR_UDE);

    U2DICR = 0x0;
    U2DISR = 0xfeffffff;
#ifdef CONFIG_CPU_MONAHANS_LV
    //U2DICR2 = 0x0;
    //U2DISR2 = 0xfeffffff;
    U2DOTGCR &= ~U2DOTGCR_ULE;
    U2DOTGICR = 0x0;
    U2DOTGISR = 0x00037f7f;
#endif
    XllpU2DDisable(&xllp_u2d);

#ifdef CONFIG_CPU_MONAHANS_LV
    U2DOTGCR |= U2DOTGCR_UTMID;
#endif

        /* Disable clock for USB device */
    //*(unsigned long*)0x4134000c &= ~0x100000;
    CKENA &= ~(0x40);
    /* Bit 39 is HSIO2 clock. */
    CKENB &= ~(0x1<<9);

}

static void u2d_soft_dis(int enable)
{
#if 0
#ifdef xjCONFIG_CPU_MONAHANS_LV
    if (enable) {
        /* enable D+/D- pulldown resistor */
        ulpi_reg_write(ULPI_OTG_CONTROL_SET, ULPI_OC_DPPULLDOWN);
        ulpi_reg_write(ULPI_OTG_CONTROL_SET, ULPI_OC_DMPULLDOWN);
    } else {
        /* disable D+/D- pulldown resistor */
        ulpi_reg_write(ULPI_OTG_CONTROL_CLEAR, ULPI_OC_DPPULLDOWN);
        ulpi_reg_write(ULPI_OTG_CONTROL_CLEAR, ULPI_OC_DMPULLDOWN);
    }
#else
    if (enable) {
        U2DCR |= U2DCR_ADD;
        msleep(6);
    } else {
        U2DCR &= ~U2DCR_ADD;
        msleep(6);
    }
#endif
#endif
}

/*  enable the udc at the source */
static void u2d_enable(void)
{
    ASCR &=0x7fffffff;

    XllpU2DInitHandle(&xllp_u2d, (P_XLLP_U2D_REGISTERS_T)0x54100000, (P_XLLP_VUINT32_T)0x40E10000, (P_XLLP_GPIO_T)0x40E00000, (P_XLLP_MFP_RM_DB_ID_T)0x5c012000);
                //U2D_BASE, MFP_BASE, GPIO_BASE, XLLP_MFP_RM_DATABASE);
    OSCC |= 0x800; //CLK_POUT enable
    CKENB |= (0x1<<9);
    CKENA |= (0x1<<6);
    XllpU2DClearAllConfigure(&xllp_u2d);

    /* configure the MFP */
    XllpU2DPlatformInit(&xllp_u2d);

    /* U2D Endpoints */
    u2d_configure(usb_speed, 1);

    /* enable endpoint 0, A, B's Packet Complete Interrupt. */
    U2DICR = U2DINT_CC | U2DINT_RS | U2DINT_RU | U2DINT_SU | U2DINT_DPE |  \
        (U2DINT_SPACKETCMP | U2DINT_PACKETCMP | U2DINT_FIFOERR);

    /* clear the interrupt status/control registers */
    U2DISR = 0xffffffff;

    U2DEN0 = 0x02000000;

    u2d_soft_dis(0);

#ifdef CONFIG_CPU_MONAHANS_LV
    U2DOTGCR = U2DOTGCR_ULAF|U2DOTGCR_ULE;
#endif

    XllpU2DDisable(&xllp_u2d);
    XllpU2DEnable(&xllp_u2d);
    //XllpU2DEnable(&xllp_u2d);

    if (U2DCR & U2DCR_EMCE) {
        DBGOUT(printf("%s:Endpoint Memory Config Error\n", __FUNCTION__));
    }
    else {
        DBGOUT(printf("Endpoint Memory Config OK and CKENA = %x\n",CKENA));
    }

    if( (U2DCR & U2DCR_UDA) == 0) {
        /* There's a reset on the bus,
         * clear the interrupt bit and keep going
         */
        DBGOUT(printf("wait for U2D to be active\n"));
    }

    dump_u2d_reg();
}

void u2d_start(void)
{
    u2d_enable();
    soft_connect_hook(1);
}
